import os

from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtXml import *

##
# Tileset widget: load a tileset image
# Provide a cursor to select a tile within the tileset image
#
class Tileset(QGraphicsView):
	TILESIZE = 16 #default tile size

	class MyScene(QGraphicsScene):
		def __init__(self):
			QGraphicsScene.__init__(self)
			self.cursor = None
			self.selection_cursor = None
			self.build_cursors()

			self.nb_line = -1
			self.nb_col = -1
			self.current_tile_id = 0

		##
		# Add selection cursor in the scene
		#
		def build_cursors(self):
			if self.cursor != None:
				self.removeItem(self.cursor)
			if self.selection_cursor != None:
				self.removeItem(self.selection_cursor)

			rect = QRectF(0, 0, Tileset.TILESIZE, Tileset.TILESIZE)
			pen = QPen(Qt.red, 1, Qt.SolidLine)
			brush = QBrush(QColor.fromRgb(255, 0, 0, 128))

			self.cursor = self.addRect(rect, pen, brush)
			self.cursor.setZValue(9000)

			self.selection_cursor = self.addRect(rect, pen)
			self.selection_cursor.setZValue(9001)

		##
		# Set tileset background image
		#
		def set_image(self, image):
			# set tileset size
			self.nb_line = image.height() / Tileset.TILESIZE
			self.nb_col = image.width() / Tileset.TILESIZE

			img_tileset = self.addPixmap(image)
			img_tileset.setZValue(0)
			img_tileset.setPos(0, 0)

		##
		# Get selected tile ID
		#
		def get_current_tile_id(self):
			return self.current_tile_id

		## event callbacks

		def mouseMoveEvent(self, event):
			x = int(event.scenePos().x()) / Tileset.TILESIZE
			y = int(event.scenePos().y()) / Tileset.TILESIZE
			if 0 <= x < self.nb_col and 0 <= y < self.nb_line:
				self.cursor.setPos(x * Tileset.TILESIZE, y * Tileset.TILESIZE)
				self.cursor.setVisible(True)
			else:
				self.cursor.setVisible(False)


		def mousePressEvent(self, event):
			x = int(event.scenePos().x()) / Tileset.TILESIZE
			y = int(event.scenePos().y()) / Tileset.TILESIZE
			if 0 <= x < self.nb_col and 0 <= y < self.nb_line:
				self.current_tile_id = y * self.nb_col + x + 1
				self.selection_cursor.setPos(x * Tileset.TILESIZE, y * Tileset.TILESIZE)
				self.emit(SIGNAL("tile_selected"), self.current_tile_id)


		def focusInEvent(self, event):
			self.cursor.setVisible(True)

		def focusOutEvent(self, event):
			self.cursor.setVisible(False)

	def __init__(self):
		self.scene = self.MyScene()
		self.scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex)
		QGraphicsView.__init__(self, self.scene)

		# view properties
		self.setMouseTracking(True)
		self.setBackgroundBrush(QBrush(Qt.black, Qt.Dense7Pattern))

		self.tiles = []
		self.filename = ""

	##
	# Load a tileset from an XML document
	#
	def load_from_xml(self, filename):

		content = open(filename).read()
		xml_document = QDomDocument()
		success, error_msg, line, col = xml_document.setContent(content)
		if not success:
			raise IOError("%s (line %d, column %d)" % (error_msg, line, col))

		# parse attributes
		root = xml_document.documentElement()
		Tileset.TILESIZE = int(root.attribute("tile_size"))
		path = os.path.dirname(filename)

		# image path is relative to tileset, not current directory
		image_filename = os.path.join(path, str(root.attribute("image")))
		if not os.path.isfile(image_filename):
			raise IOError("tileset image %s not found" % image_filename)

		# set scene background image
		self.scene.set_image(QPixmap(image_filename))

		# split image into tiles
		del self.tiles[:]
		image = QImage(image_filename)
		for i in xrange(self.scene.nb_line * self.scene.nb_col):
			left = (i % self.scene.nb_col) * self.TILESIZE
			top = (i / self.scene.nb_col) * self.TILESIZE

			tile = image.copy(left, top, self.TILESIZE, self.TILESIZE)
			self.tiles.append(tile)

		# rebuild cursor to fit new TILZSIZE dimension
		self.scene.build_cursors()
		self.emit(SIGNAL("tilesetLoaded()"))
		self.filename = filename

	##
	# Get a tile from the tileset
	# @param tile_id: tile id within the current tileset
	# @return QImage tile
	#
	def get_tile_by_id(self, tile_id):
		return self.tiles[tile_id - 1]


	def get_selected_tile_id(self):
		return self.scene.get_current_tile_id()

	def get_filename(self):
		return self.filename
